﻿/*****************************************介绍*****************************************
 * 作者：Ywh
 * 创建时间：2024-05-25 16:16:05
 * 功能：公共Mono模块
 *      1、为全局提供一个Mono，可以开启协程
**************************************************************************************/

using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

namespace Game.Framework
{
    [InternalModule(2)]
    public class CommonMono : SingletonMonoAuto<CommonMono>, IModule
    {
        private void Update() => EventCenter.Instance.Trigger(EventName.MonoUpdate);

        private void FixedUpdate() => EventCenter.Instance.Trigger(EventName.MonoFixedUpdate);

        private void LateUpdate() => EventCenter.Instance.Trigger(EventName.MonoLateUpdate);

        private void OnApplicationPause(bool pauseStatus) => EventCenter.Instance.Trigger(EventName.MonoOnApplicationPause, pauseStatus);

#if DEBUG
        private void OnGUI() => EventCenter.Instance.Trigger(EventName.MonoOnGUI);
#endif

        private InitResult m_InitResult;

        void IModule.Init()
        {
            m_InitResult = new InitResult(E_ResultType.Success);
        }

        InitResult IModule.GetInitResult() => m_InitResult;

        void IModule.Clear() { }

        #region Http请求接口

        /// <summary>异步获取精灵图片</summary>
        public void GetSpriteAsync(string url, Action<Sprite> complate, int retryCount = 3)
        {
            StartCoroutine(ReallyLoadRes_Get<Texture>(url, tex =>
            {
                if (tex == null)
                    complate?.Invoke(null);
                else
                {
                    Sprite sp = Sprite.Create(tex as Texture2D, new Rect(0, 0, tex.width, tex.height), Vector2.one * .5f);
                    complate?.Invoke(sp == null ? null : sp);
                }
            }, error =>
            {
                if (retryCount <= 0)
                    complate?.Invoke(null);
                else
                {
                    LogManager.LogError($"error! retry:{retryCount} msg:{error}", "GetSpriteAsync");
                    GetSpriteAsync(url, complate, retryCount - 1);
                }
            }));
        }

        /// <summary>加载云资源</summary>
        /// <typeparam name="T">类型只能是string、byte[]、Texture、AssetBundle 不能是其他类型 目前不支持</typeparam>
        /// <param name="url">资源路径、要自己加上协议 http、ftp、file</param>
        /// <param name="callBack">加载成功的回调函数</param>
        /// <param name="failCallBack">加载失败的回调函数</param>
        public void SendGetRequest<T>(string url, UnityAction<T> callBack, UnityAction<string> failCallBack) where T : class
        {
            StartCoroutine(ReallyLoadRes_Get(url, callBack, failCallBack));
        }

        /// <summary>请求网络资源的大小</summary>
        public void SendGetSizeRequest(string url, Action<long> succHandle, Action<string> failHandle = null)
        {
            StartCoroutine(GetNetAssetSize_Internal(url, succHandle, failHandle));
        }

        public void SendPostReqest(NetPostData data)
        {
            StartCoroutine(ReallyLoadRes_Post(data));
        }

        private IEnumerator GetNetAssetSize_Internal(string reqUrl, Action<long> succHandle, Action<string> failHandle)
        {
            if (string.IsNullOrEmpty(reqUrl))
                failHandle?.Invoke("请求地址不能为空！");
            var headReq = UnityWebRequest.Head(reqUrl);
            headReq.SendWebRequest();
            while (!headReq.isDone) { yield return null; }

            if (headReq.result != UnityWebRequest.Result.Success)
            {
                failHandle?.Invoke($"请求响应错误，请检查！！reqUrl:{reqUrl} result:{headReq.result} code:{headReq.responseCode} error:{headReq.error}");
                yield break;
            }
            string totalSizeStr = headReq.GetResponseHeader("Content-Length");
            long total = long.Parse(totalSizeStr);
            LogManager.Log($"资源大小：{total / Mathf.Pow(1024, 2):f5} MB");
            succHandle?.Invoke(total);
        }

        private IEnumerator ReallyLoadRes_Get<T>(string path, UnityAction<T> callBack, UnityAction<string> failCallBack) where T : class
        {
            Type type = typeof(T);
            //用于加载的对象
            UnityWebRequest req = null;
            if (type == typeof(string) || type == typeof(byte[]))
                req = UnityWebRequest.Get(path);
            else if (type == typeof(Texture))
                req = UnityWebRequestTexture.GetTexture(path);
            else if (type == typeof(AssetBundle))
                req = UnityWebRequestAssetBundle.GetAssetBundle(path);
            else
            {
                failCallBack?.Invoke($"不支持的类型：{type}");
                yield break;
            }

            yield return req.SendWebRequest();
            //如果加载成功 
            if (req.result == UnityWebRequest.Result.Success)
            {
                if (type == typeof(string))
                    callBack?.Invoke(req.downloadHandler.text as T);
                else if (type == typeof(byte[]))
                    callBack?.Invoke(req.downloadHandler.data as T);
                else if (type == typeof(Texture))
                    callBack?.Invoke(DownloadHandlerTexture.GetContent(req) as T);
                else if (type == typeof(AssetBundle))
                    callBack?.Invoke(DownloadHandlerAssetBundle.GetContent(req) as T);
            }
            else
                failCallBack?.Invoke($"请求响应错误，请检查！！reqUrl:{path} result:{req.result} code:{req.responseCode} error:{req.error}");

            //释放UWQ对象
            req.Dispose();
        }

        private IEnumerator ReallyLoadRes_Post(NetPostData data)
        {
            LogManager.Log($"Post 请求，url:{data.url} headers:{data.reqHeaders} params:{data.reqParams} isEncrypt:{data.isEncrypt}", "CommonMono");
            using (UnityWebRequest req = new UnityWebRequest(data.url, UnityWebRequest.kHttpVerbPOST))
            {
                if (data.timeout > 0) req.timeout = data.timeout;
                if (data.reqParams != null)
                {
                    string reqParams = data.reqParams.ToString();
                    if (data.isEncrypt)
                        req.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(Utility.CryptoUtils.AesEncrypt(reqParams)));
                    else
                        req.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(reqParams));
                }
                if (data.reqHeaders != null)
                {
                    foreach (var item in data.reqHeaders)
                        req.SetRequestHeader(item.Key, item.Value.ToString());
                }
                req.downloadHandler = new DownloadHandlerBuffer();
                yield return req.SendWebRequest();
                if (req.result == UnityWebRequest.Result.Success)
                    data.callBack?.Invoke(req.downloadHandler.data);
                else
                    data.failCallBack?.Invoke($"请求响应错误，请检查！！reqUrl:{data.url} result:{req.result} code:{req.responseCode} error:{req.error}");
            }
        }

        public struct NetPostData
        {
            public string url;
            public JObject reqParams;
            public JObject reqHeaders;
            public UnityAction<byte[]> callBack;
            public UnityAction<string> failCallBack;
            public bool isEncrypt;
            public int timeout;
        }

        #endregion

        #region 协程相关接口

        private IEnumerator DelayHandleInternal(Func<bool> condition, Action action, float waitTime, float delayTime)
        {
            while (!condition())
            {
                yield return waitTime <= 0.001f ? null : new WaitForSeconds(waitTime);
            }
            yield return delayTime <= 0.001f ? null : new WaitForSeconds(delayTime);
            action?.Invoke();
        }

        /// <summary>延迟delayTime秒后执行action</summary>
        public void DelayHandle(float delayTime, Action action)
        {
            StartCoroutine(DelayHandleInternal(() => true, action, 0, delayTime));
        }

        /// <summary>满足条件condition后执行action</summary>
        /// <param name="waitTime">等待条件满足时的间隔时间</param>
        public void DelayHandle(Func<bool> condition, Action action, float waitTime = 0f)
        {
            StartCoroutine(DelayHandleInternal(condition, action, waitTime, 0));
        }

        /// <summary>满足条件condition后等待delayTIme秒后执行action</summary>
        /// <param name="waitTime">等待条件满足时的间隔时间</param>
        public void DelayHandle(Func<bool> condition, Action action, float waitTime, float delayTime = 0f)
        {
            StartCoroutine(DelayHandleInternal(condition, action, waitTime, 0));
        }

        #endregion
    }
}